import { useState } from "react";
import "./App.scss";
import SparkMD5 from "spark-md5";
import axios from "axios";
// App 组件：演示大文件分片上传、断点续传与秒传的基础流程
const App = () => {
  // 分片上传: 把一个大文件拆分成小文件 分组上传
  // 断点续传: 一个文件没有完全上传完毕， 下一次上传接着上一次的进度
  // 文件秒传： 文件上传过， 下一次上传直接秒传成功， 无需等待上传

  /****** 点击上传 *******/
  // 通过原生 input[type=file] 唤起系统文件选择器
  const handleSelect = () => {
    // 创建input
    const input = document.createElement("input");
    // 设置input 的类型 文件类型
    input.type = "file";
    //  开启多选
    input.multiple = true;
    // 模仿 用户点击 input
    input.click();
    // 当选中的文件 发生了触发该事件
    input.addEventListener("change", () => {
      const files = input.files;
      // 准备上传
      handleUpload(files!);
    });
  };

  /***** 拖拽 ******/
  const [text, setText] = useState("点击上传文件|拖住文件上传");
  const [isActive, setIsActive] = useState(false);
  // 拖入上传区域
  const dragEnter = () => {
    setText("点击上传文件|松手文件上传");
    setIsActive(true);
  };
  // 离开上传区域
  const dragLeave = () => {
    setText("点击上传文件|拖住文件上传");
    setIsActive(false);
  };

  /******** 处理上传逻辑 **********/
  /**
   * 分片上传主流程：
   * 1) 计算文件 MD5（用于秒传、断点续传和分片命名）
   * 2) 后端校验文件是否已上传（秒传）
   * 3) 生成分片清单并并发上传
   * 4) 通知后端合并分片
   */
  const handleUpload = async (files: FileList) => {
    for (const file of files) {
      // 1. 计算文件 MD5（用于唯一标识文件）
      const md5 = await getFileMD5(file);
      // 2. 校验文件是否已上传（后端应根据 md5 判断是否存在）
      const checkFile = await axios.post('/api/check/file');
      if(checkFile.data.data.uploaded) {
        alert('文件上传过')
        return 
      }
      // 3. 生成分片
      // file 就是文件本身
      const chunkSize = 1024 * 1024 * 2; // 每片大小：2MB
      const totalSize = file.size; // 文件总大小
      const total = Math.ceil(totalSize / chunkSize); // 分片总数
      const ext = file.name.slice(file.name.lastIndexOf('.')) // 原始扩展名
      const chunkList: { file: Blob; name: string }[] = [];
      for (let i = 0; i < total; i++) {
        const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
        chunkList.push({
          file: chunk,
          name: `${md5}-${i}${ext}`
        });
      }
      // 4. 并发上传所有分片
      const requestMap = chunkList.map(v => {
        const formData = new FormData();
        formData.append('file', v.file, file.name); // 分片二进制数据
        formData.append('name', v.name) // 分片名：md5-序号.扩展名
        return axios.post('/api/upload', formData)
      })

      // 等待所有分片上传完成
      const uploadResult = await Promise.all(requestMap)
      // 5. 通知后端进行分片合并
      const mergeResult = await axios.post('/api/merge/file', {
        extname: ext,
        hash: md5,
        len: total,
        originName: file.name,
        size: file.size
      })
      

      console.log('uploadResult', uploadResult)
      console.log('mergeResult', mergeResult)
    }
  };

  /**
   * 按 2MB 分片顺序读取文件并计算 MD5。
   * 返回 Promise<string>，resolve 为文件的 MD5 值。
   */
  function getFileMD5(file: File) {
    return new Promise((resolve) => {
      const chunkSize = 2097152, // Read in chunks of 2MB
        chunks = Math.ceil(file.size / chunkSize),
        spark = new SparkMD5.ArrayBuffer(),
        fileReader = new FileReader();

      let currentChunk = 0;

      fileReader.onload = function (e) {
        // 将当前分片内容追加到 MD5 计算器
        spark.append(e.target!.result as ArrayBuffer); // Append array buffer
        currentChunk++;

        if (currentChunk < chunks) {
          // 继续读取下一片
          loadNext();
        } else {
          // 全部分片读取完成，得到 MD5
          resolve(spark.end())
        }
      };

      fileReader.onerror = function () {
        console.warn("oops, something went wrong.");
      };

      function loadNext() {
        const start = currentChunk * chunkSize,
          end = start + chunkSize >= file.size ? file.size : start + chunkSize;

        fileReader.readAsArrayBuffer(file.slice(start, end));
      }

      loadNext();
    });
  }

  return (
    <div>
      <div
        className={isActive ? "upload active" : "upload"}
        onClick={() => handleSelect()}
        onDragEnter={() => dragEnter()}
        onDragLeave={() => dragLeave()}
        // 阻止默认事件，允许 drop 触发
        onDragOver={(e) => e.preventDefault()}
        onDrop={(e) => {
          e.preventDefault();
          const files = e.dataTransfer.files;
          // 准备上传
          handleUpload(files);
        }}
      >
        <h3>{text}</h3>
      </div>
      {/* 预留：如果未来要切换为“选择后手动点击开始上传”，可绑定 handleUpload */}
      <button>开始上传</button>
      <div className="progress">
        <h3>进度</h3>
        {/* 预留：展示每个文件/分片进度，可结合 axios onUploadProgress 实现 */}
      </div>
    </div>
  );
};

export default App;
